package utils

import (
	"autojs-tool-go/domain"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
	"sync"
	"time"
)

// WsMutex 读写锁
var WsMutex sync.RWMutex

// Relation save websocket connection
var Relation = make(map[string]*WsConnection)

// WsConnection 连接对象
type WsConnection struct {
	Id        string // id
	WsSocket  *websocket.Conn
	InChan    chan *domain.AjMessage // 读队列
	OutChan   chan *domain.AjMessage // 写队列
	Mutex     sync.Mutex             // 避免重复关闭通道
	IsClosed  bool                   // 是否关闭
	CloseChan chan byte              // 关闭通知
	UserInfo  *domain.ClientInfo     // 用户信息
}

// RegisterConnect 注册链接
func RegisterConnect(wsConn *WsConnection) {
	WsMutex.Lock()
	Relation[wsConn.Id] = wsConn
	WsMutex.Unlock()
	go wsConn.WsReadLoop()
	go wsConn.WsWriteLoop()
	go wsConn.ProcLoop()
}

// WsReadLoop ...
func (wsConn *WsConnection) WsReadLoop() {
	for {
		_, data, err := wsConn.WsSocket.ReadMessage()
		if err != nil {
			goto error
		}

		var body = new(domain.AjMessage)
		body.MessageDateTime = domain.TimeStr(time.Now())
		_ = json.Unmarshal(data, body)

		// 放入请求队列
		select {
		case wsConn.InChan <- body:
		case <-wsConn.CloseChan:
			fmt.Println("wsReadLoop close websocket")
			goto closed
		}
	}
error:
	wsConn.wsClose()
closed:
}

// WsWriteLoop ...
func (wsConn *WsConnection) WsWriteLoop() {
	for {
		select {
		//取一个应答
		case msg := <-wsConn.OutChan:
			//将msg转化成字节数组
			//msg := msgJson{M_str: "xxxxx"}
			//msgbyte, err := json.Marshal(msg)
			if err := wsConn.WsSocket.WriteJSON(msg); err != nil {
				goto error
			}
		case <-wsConn.CloseChan:
			goto closed
		}
	}

error:
	wsConn.wsClose()
closed:
}

// WsWrite ...
func (wsConn *WsConnection) WsWrite(message *domain.AjMessage) error {
	select {
	case wsConn.OutChan <- message:
	case <-wsConn.CloseChan:
		return errors.New("websocket OutChan closed")
	}
	return nil
}

// WsRead ...
func (wsConn *WsConnection) WsRead() (*domain.AjMessage, error) {
	select {
	case msg := <-wsConn.InChan:
		return msg, nil
	case <-wsConn.CloseChan:
	}
	return nil, errors.New("websocket InChan closed")
}

// ProcLoop 心跳检测
func (wsConn *WsConnection) ProcLoop() {
	// 启动一个goroutine 发送心跳
	go func() {
		for {
			time.Sleep(50 * time.Second)
			if err := wsConn.WsSocket.WriteMessage(websocket.PingMessage, []byte("heartbeat")); err != nil {
				logrus.Errorf("heartbeat fail %v", err.Error())
				wsConn.wsClose()
				break
			}
		}
	}()
}

func (wsConn *WsConnection) wsClose() {
	_ = wsConn.WsSocket.Close()
	wsConn.Mutex.Lock()
	defer wsConn.Mutex.Unlock()
	if !wsConn.IsClosed {
		wsConn.IsClosed = true
		close(wsConn.CloseChan)
	}
	delete(Relation, wsConn.Id)
	logrus.Println("链接关闭", wsConn.Id)
}
